#include <thread>
#include <iostream>
#include <mutex>
#include <condition_variable>
#include <list>

using std::cout;
using std::endl;

template<typename T>
class SyncQueue{
    bool IsFull() const
    {
        return m_queue.size() == m_maxSize;
    }
    bool IsEmpty() const
    {
        return m_queue.empty();
    }

    public:
    SyncQueue(int maxSize) :m_maxSize(maxSize) {}

    void Put(const T &x)
    {
        std::lock_guard<std::mutex> locker(m_mutex); // 保护; 控制 卫兵
        while(IsFull())
        {
            cout << "缓冲区满了， 需要等待" << endl;
            m_notFull.wait(m_mutex);
        }

        m_queue.push_back(x);
        m_notEmpty.notify_one();
    }

    void Take(T &x)
    {
        std::lock_guard<std::mutex> locket(m_mutex);
        while(IsEmpty())
        {
            cout << "缓冲区空了，需要等待" << endl;
            m_notEmpty.wait(m_mutex);
        }
        x = m_queue.front();
        m_queue.pop_front();
        m_notFull.notify_one();
    }

    bool Empty()
    {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.empty();
    }

    bool Full()
    {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size() == m_maxSize;
    }

    size_t Size()
    {
        std::lock_guard<std::mutex> locker(m_mutex);
        return m_queue.size();
    }

    int Count()
    {
        return m_queue.size();
    }
private:
    std::list<T> m_queue; //缓冲区
    std::mutex m_mutex; //互斥量
    std::condition_variable_any m_notEmpty; //不为空的条件变量
    std::condition_variable_any m_notFull; //没有满的条件变量 //等会儿在写一下官方的例子
    int m_maxSize; //同步队列最大的size
};

SyncQueue<int> queue(10);
void fun1() //消费者
{
    cout << "吃货1: " << std::this_thread::get_id() << endl;
    int x = 0;
    while(x != 10) { //吃到10 算我输
        queue.Take(x);
        cout << " |  吃到了 :" << x ;
        //要延时一下
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    cout << "bye" << std::this_thread::get_id()<< endl;
}

void fun2() //生产者
{
    cout << "产货1: " << std::this_thread::get_id()<< endl;;

    int x = 0;
    while (! queue.Full())
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        queue.Put(x++);
        cout << "| 产出 :" << x;
    }

    cout << "bye" << std::this_thread::get_id()<< endl;
}

void fun(){}


int main(void)
{
    SyncQueue<int> queue(10);

    std::thread t1(fun1);
    std::thread t2(fun2);

    t1.join();
    t2.join();

    return 0;
}
